/**
 * 
 */
package registnet.courseDemand;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import registnet.dao.ClassroomView;
import registnet.dao.ClassroomViewPeer;
import registnet.dao.CourseView;
import registnet.dao.CourseViewPeer;
import registnet.dao.SectionLaboratory;
import registnet.dao.SectionLecture;
import registnet.dao.SectionView;
import registnet.dao.Slot;
import registnet.dao.StudentView;
import registnet.dao.StudentViewPeer;
import registnet.dao.ValidSubjectsViewPeer;

/**
 * @author Dexter Ryan Carrasco
 */
/**
 * @author Dexter Ryan Carrasco
 */
/**
 * @author Dexter Ryan Carrasco
 */
public class StudentClusteringAlgorithm implements CourseDemandConstants {
	/** The Map of room categories and rooms */
	private Map<String, List<ClassroomView>> roomsList;

	/** The Map of String and Courses */
	private Map<String, CourseView> subjectsList;

	/** The List of degree courses */
	private List<String> degreeCoursesList;

	/** The List of non-major courses */
	private List<String> nonMajorCoursesList;

	/** The List of Student Clusters */
	private List<StudentCluster> studentClusters;

	/** The List of students */
	private List<StudentView> students;

	/** The List of subjects with missing information */
	private final List<String> subjectsWithMissingInfo;

	/** The List of students with unscheduled courses */
	private final List<StudentView> studentsWithUnscheduledCourses;

	/** The final List of Students wiht unscheduled courses */
	private final List<StudentView> finalStudentsWithUnscheduledCourses;

	/** The List of courses with time and day not considered by the program */
	private final List<String> notYetImplementedCourses;

	/** The map of course and number of students that did not get the course */
	private final Map<String, Integer> unscheduledStudentsMap;

	/**
	 * Default Constructor
	 */
	public StudentClusteringAlgorithm() {
		this.subjectsWithMissingInfo = new ArrayList<String>();
		this.studentsWithUnscheduledCourses = new ArrayList<StudentView>();
		this.notYetImplementedCourses = new ArrayList<String>();
		this.unscheduledStudentsMap = new HashMap<String, Integer>();
		this.finalStudentsWithUnscheduledCourses = new ArrayList<StudentView>();
	}

	/**
	 * Assigns the section name of one schedule
	 * 
	 * @param lectureSections
	 * @return
	 */
	private List<String> assignSections(
			final List<SectionLecture> lectureSections) {
		List<String> sameSections = new ArrayList<String>();
		final List<String> sectionsAssigned = new ArrayList<String>();

		for (final Iterator<SectionLecture> lectIter = lectureSections
				.iterator(); lectIter.hasNext();) {
			final SectionLecture lecture = lectIter.next();
			final String section = ScheduleUtility.getSectionName(lecture
					.getTimeInt(), lecture.getDaysInt());
			lecture.setLecSectionName(section);

			if (sectionsAssigned.contains(section)
					&& !sameSections.contains(section)) {
				sameSections.add(section);
			} else {
				if (!sectionsAssigned.contains(section)) {
					sectionsAssigned.add(section);
				}
			}
		}

		if (sameSections.size() < 1) {
			sameSections = null;
		}

		return sameSections;
	}

	/**
	 * Cluster the students according to their major courses
	 * 
	 * @param students
	 * @return the List of student clusters
	 * @throws Exception
	 */
	private List<StudentCluster> clusterStudents(
			final List<StudentView> students) throws Exception {
		this.studentClusters = new ArrayList<StudentCluster>();

		for (final Iterator<StudentView> studentIter = students.iterator(); studentIter
				.hasNext();) {
			final StudentView student = studentIter.next();
			boolean isInserted = false;

			final RecommendedCourses courses = new RecommendedCourses();
			courses.setRecommendedCourses(ValidSubjectsViewPeer
					.getValidCoursesFor(student.getStudentNumber()));
			courses.categorized(this.nonMajorCoursesList);
			student.setRecommendedCourses(courses);

			for (final Iterator<StudentCluster> clusterIter = this.studentClusters
					.iterator(); clusterIter.hasNext();) {
				final StudentCluster cluster = clusterIter.next();

				if (cluster.hasSameMajorCourses(student)) {
					cluster.addStudent(student);
					isInserted = true;
					break;
				}
			}

			if (!isInserted) {
				final StudentCluster newCluster = new StudentCluster();
				newCluster.addStudent(student);
				newCluster.setCommonMajorCourses(student
						.getRecommendedCourses().getMajorCourses());

				this.studentClusters.add(newCluster);
			}
		}

		/* sorts the clusters by size */
		CourseDemandUtility.sortClustersBySize(this.studentClusters);

		return this.studentClusters;
	}

	/**
	 * Creates a new section schedule based on the vacant time of the given
	 * student
	 * 
	 * @param student
	 * @param course
	 * @return
	 */
	private boolean createScheduleFor(final StudentView student,
			final CourseView course) {
		boolean isInserted = false;
		final Schedule lectureSkeds = new Schedule();

		final String lecRoomCategory = course.getLectureRoomCategory();
		final String labRoomCategory = course.getLaboratoryRoomCategory();
		final List<ClassroomView> possibleLecRooms = new ArrayList<ClassroomView>();
		final List<ClassroomView> possibleLabRooms = new ArrayList<ClassroomView>();
		final String courseCode = course.getCourseCode();

		final int lecDaysPerWeek = course.getLectureSessionsPerWeek();

		if (lecDaysPerWeek == 0) {
			return isInserted;
		}

		final int lecTimePerSession = Math.round(course
				.getLectureHoursPerWeek()
				* 2 / lecDaysPerWeek);

		final List<SectionLecture> lectureSections = course
				.getLectureSections();

		if (course.getHasLaboratory()) {
			if (course.getNumOfLab() < course.getPredictedNumOfLab()) {

				final int maxLabPerLec = course.getMaxLabPerLec();
				final int labDaysPerWeek = course
						.getLaboratorySessionsPerWeek();

				if (labDaysPerWeek == 0) {
					// System.out.println(course.getCourseCode()
					// + " has invalid laboratory sessions per week.");
					return isInserted;
				}

				final int labTimePerSession = Math.round(course
						.getLaboratoryHoursPerWeek()
						* 2 / labDaysPerWeek);

				for (final Iterator<SectionLecture> lectIter = lectureSections
						.iterator(); lectIter.hasNext();) {

					final SectionLecture lecture = lectIter.next();
					final List<SectionLaboratory> labSections = lecture
							.getLabSections();
					final Schedule tempSked = new Schedule();

					final ClassroomView candidateLecRoom = lecture.getRoom();
					if (candidateLecRoom != null
							&& !possibleLecRooms.contains(candidateLecRoom)) {
						possibleLecRooms.add(candidateLecRoom);
					}

					// copy lecture sked in lecture skeds
					lectureSkeds.addScheduleWithoutCheck(lecture.getDaysInt(),
							lecture.getTimeInt(), lecture.getCourseCode());

					if (labSections.size() < maxLabPerLec
							&& student.isVacant(lecture.getDaysInt(), lecture
									.getTimeInt())) {

						// TODO: schedule a section
						tempSked.addScheduleWithoutCheck(lecture.getDaysInt(),
								lecture.getTimeInt(), lecture.getCourseCode());

						// iterateThroughLab(labsections, tempsked,
						// possibleLabrooms)

						for (final Iterator<SectionLaboratory> labIter = labSections
								.iterator(); labIter.hasNext();) {

							final SectionLaboratory lab = labIter.next();
							// this is done so that no lab sections are
							// scheduled at the same time
							tempSked.addScheduleWithoutCheck(lab.getDaysInt(),
									lab.getTimeInt(), lab.getCourseCode());

							final ClassroomView candidateLabRoom = lab
									.getRoom();
							if (candidateLabRoom != null
									&& !possibleLabRooms
											.contains(candidateLabRoom)) {
								possibleLabRooms.add(candidateLabRoom);
							}
						}

						// find a sked not in conflict with the scheduled
						// lab sections and lecture in the cluster
						tempSked.copy(student.getStudentSchedule());

						// TODO: copyRoom sked

						final List<SchedulePair> possibleLabSkeds = tempSked
								.findVacant(labDaysPerWeek, labTimePerSession,
										lecture.getCourseCode(),
										this.notYetImplementedCourses, course
												.getSubjectType());

						if (possibleLabSkeds != null) {

							// get the best schedule
							final RoomAndSkedPair match = this
									.getRoomAndSked(
											labRoomCategory,
											possibleLabSkeds,
											possibleLabRooms,
											courseCode,
											course
													.getMaximumNumberOfLaboratoryStudents());

							if (match != null) {
								final SchedulePair labSked = match
										.getSchedule();
								final ClassroomView labRoom = match.getRoom();
								final SectionLaboratory labSection = new SectionLaboratory(
										lecture.getCourseCode(), lecture
												.getLecSectionName());

								labSection.setDaysInt(labSked.getDaysInt());
								labSection.setTimeInt(labSked.getTimeInt());
								labSection.setLabSectionName(lecture
										.getLecSectionName()
										+ "-L");
								labSection.addStudent(student);
								labSection.setRoom(labRoom);

								course.setNumOfLab(course.getNumOfLab() + 1);
								lecture.addLabSection(labSection);
								lecture.addStudent(student);
								isInserted = true;

								// updates the cluster sked
								student.addSchedule(labSked.getDaysInt(),
										labSked.getTimeInt(), labSection
												.getCourseCode());
								student.addSchedule(lecture.getDaysInt(),
										lecture.getTimeInt(), lecture
												.getCourseCode());
								break;
							} else {
								System.out
										.println("LAB ROOM NOT FOUND^^^^^^^^^^^^^^^^^^^^^^"
												+ course.getCourseCode());
							}

						}
					}
				}

				// TODO:if(di pa rin na sked) gawa na ng bago lecture
				if (!isInserted) {
					int maxNumOfLec = 0;

					// checks whether max number of sections to be scheduled
					// is
					// specified or not
					if (course.getMaxNumberOfLectureSections() == -1) {
						maxNumOfLec = course.getPredictedNumOfLec();
					} else {
						maxNumOfLec = Math.min(course
								.getMaxNumberOfLectureSections(), course
								.getPredictedNumOfLec());
					}
					if (course.getNumOfLec() < maxNumOfLec) {
						final Schedule tempSked = new Schedule();
						tempSked.copy(lectureSkeds);
						tempSked.copy(student.getStudentSchedule());

						final List<SchedulePair> possibleLecSkeds = tempSked
								.findVacant(lecDaysPerWeek, lecTimePerSession,
										course.getCourseCode(),
										this.notYetImplementedCourses, course
												.getSubjectType());

						if (possibleLecSkeds != null) {

							RoomAndSkedPair match = this.getRoomAndSked(
									lecRoomCategory, possibleLecSkeds,
									possibleLecRooms, courseCode,
									course.getMaximumNumberOfLectureStudents());

							if (match == null) {
								System.out
										.println("LEC ROOM NOT FOUND^^^^^^^^^^^^^^^^^^^^^^"
												+ course.getCourseCode());
								return false;
							}

							final SchedulePair lecSked = match.getSchedule();
							final ClassroomView lecRoom = match.getRoom();

							final SectionLecture lecSection = new SectionLecture(
									course.getCourseCode(), "SECTION");
							lecSection.setDaysInt(lecSked.getDaysInt());
							lecSection.setTimeInt(lecSked.getTimeInt());
							lecSection.setRoom(lecRoom);

							tempSked.addScheduleWithoutCheck(lecSked
									.getDaysInt(), lecSked.getTimeInt(),
									lecSection.getCourseCode());

							final List<SchedulePair> possibleLabSkeds = tempSked
									.findVacant(labDaysPerWeek,
											labTimePerSession, course
													.getCourseCode(),
											this.notYetImplementedCourses,
											course.getSubjectType());

							if (possibleLabSkeds != null) {
								// get the best schedule (it is the one
								// placed
								// at the first of the list returned)

								match = this
										.getRoomAndSked(
												labRoomCategory,
												possibleLabSkeds,
												possibleLabRooms,
												courseCode,
												course
														.getMaximumNumberOfLaboratoryStudents());

								if (match != null) {
									final SchedulePair labSked = match
											.getSchedule();
									final ClassroomView labRoom = match
											.getRoom();

									final SectionLaboratory labSection = new SectionLaboratory(
											lecSection.getCourseCode(),
											lecSection.getLecSectionName());

									labSection.setDaysInt(labSked.getDaysInt());
									labSection.setTimeInt(labSked.getTimeInt());
									labSection.setLabSectionName(lecSection
											.getLecSectionName()
											+ "-L");
									labSection.addStudent(student);
									labSection.setRoom(labRoom);

									isInserted = true;

									lecSection.addLabSection(labSection);
									lecSection.addStudent(student);

									course
											.setNumOfLab(course.getNumOfLab() + 1);

									// updates the cluster sked
									student.addSchedule(labSked.getDaysInt(),
											labSked.getTimeInt(), labSection
													.getCourseCode());
									student.addSchedule(
											lecSection.getDaysInt(), lecSection
													.getTimeInt(), lecSection
													.getCourseCode());

									course.addSection(lecSection);
								} else {
									System.out
											.println("LAB ROOM NOT FOUND^^^^^^^^^^^^^^^^^^^^^^"
													+ course.getCourseCode());
									return false;
								}
							}
						}
					}
				}
			}

		} else {
			// TODO: no lab
			final Schedule tempSked = new Schedule();
			int maxNumOfLec = 0;

			// checks whether max number of sections to be scheduled
			// is
			// specified or not
			if (course.getMaxNumberOfLectureSections() == -1) {
				maxNumOfLec = course.getPredictedNumOfLec();
			} else {
				maxNumOfLec = Math.min(course.getMaxNumberOfLectureSections(),
						course.getPredictedNumOfLec());
			}

			if (course.getNumOfLec() < maxNumOfLec) {

				for (final Iterator<SectionLecture> lectIter = lectureSections
						.iterator(); lectIter.hasNext();) {

					final SectionLecture lecture = lectIter.next();

					// copy lecture sked in lecture skeds
					lectureSkeds.addScheduleWithoutCheck(lecture.getDaysInt(),
							lecture.getTimeInt(), lecture.getCourseCode());

					final ClassroomView candidateLecRoom = lecture.getRoom();

					if (candidateLecRoom != null
							&& !possibleLecRooms.contains(candidateLecRoom)) {
						possibleLecRooms.add(candidateLecRoom);
					}
				}

				tempSked.copy(lectureSkeds);
				tempSked.copy(student.getStudentSchedule());

				List<SchedulePair> possibleLecSkeds = tempSked.findVacant(
						lecDaysPerWeek, lecTimePerSession, course
								.getCourseCode(),
						this.notYetImplementedCourses, course.getSubjectType());

				if (possibleLecSkeds == null) {
					possibleLecSkeds = student.getStudentSchedule().findVacant(
							lecDaysPerWeek, lecTimePerSession,
							course.getCourseCode(),
							this.notYetImplementedCourses,
							course.getSubjectType());
				}

				if (possibleLecSkeds != null) {

					final RoomAndSkedPair match = this.getRoomAndSked(
							lecRoomCategory, possibleLecSkeds,
							possibleLecRooms, courseCode, course
									.getMaximumNumberOfLectureStudents());

					if (match != null) {
						final SchedulePair lecSked = match.getSchedule();
						final ClassroomView lecRoom = match.getRoom();

						final SectionLecture lecSection = new SectionLecture(
								course.getCourseCode(), "SECTION");
						lecSection.setDaysInt(lecSked.getDaysInt());
						lecSection.setTimeInt(lecSked.getTimeInt());
						lecSection.setRoom(lecRoom);

						isInserted = true;

						lecSection.addStudent(student);

						// updates cluster sked
						student.addSchedule(lecSection.getDaysInt(), lecSection
								.getTimeInt(), lecSection.getCourseCode());

						course.addSection(lecSection);
					} else {
						System.out
								.println("LEC ROOM NOT FOUND^^^^^^^^^^^^^^^^^^^^^^"
										+ course.getCourseCode());
						return false;
					}
				}

			}
		}

		return isInserted;
	}

	/**
	 * Divides the cluster to smaller clusters for easy processing
	 * 
	 * @param cluster
	 * @param unscheduled
	 */
	private List<StudentCluster> divideIntoMiniClusters(
			final StudentCluster cluster, final Set<CourseView> unscheduled) {
		final int clusterSize = cluster.size();
		final int min = this.getSmallestMaxStudents(unscheduled);
		final List<StudentCluster> clusters = new ArrayList<StudentCluster>();

		if (clusterSize > min) {
			System.out.println("CLUSTER of SIZE " + clusterSize
					+ " is to be further divided to CLUSTER of SIZE " + min);

			final List<StudentCluster> dividedClusters = cluster
					.divideInto(min);

			if (dividedClusters != null) {
				System.out.println("##################");
				System.out.println("CLUSTER has been divided into "
						+ dividedClusters.size() + " clusters");
				// int ctr = 1;
				for (final Iterator<StudentCluster> miniClustersIter = dividedClusters
						.iterator(); miniClustersIter.hasNext();) {
					final StudentCluster miniCluster = miniClustersIter.next();
					// System.out.println(">>>>>");
					// System.out.println("Mini Cluster " + ctr);
					this.scheduleMiniClusters(miniCluster, unscheduled);

					// ctr++;
					// System.out.println(miniCluster.toStringUnscheduled());
					// miniCluster.getSchedule().print();
					// System.out.println(">>>>>\n");
				}
				System.out.println("##################");
				clusters.addAll(dividedClusters);
			}
		} else {
			for (final Iterator<CourseView> subjIter = unscheduled.iterator(); subjIter
					.hasNext();) {
				cluster.addUnscheduledCourse(subjIter.next().getCourseCode());
			}
			clusters.add(cluster);
		}

		return clusters;
	}

	/**
	 * Gets the List of course objects given the string of course codes
	 * 
	 * @param coursesString
	 * @return
	 */
	private List<CourseView> getCourses(final List<String> coursesString) {
		final List<CourseView> courses = new ArrayList<CourseView>();

		for (final Iterator<String> strIter = coursesString.iterator(); strIter
				.hasNext();) {
			final String courseCode = strIter.next();
			final CourseView course = this.subjectsList.get(courseCode);
			if (course == null) {
				if (!this.subjectsWithMissingInfo.contains(courseCode)) {
					this.subjectsWithMissingInfo.add(courseCode);
				}
			} else {
				courses.add(course);
			}
		}

		return courses;
	}

	/**
	 * Finds a room that will fit the schedule
	 * 
	 * @param sked
	 * @param classroom
	 * @param roomCategory
	 * @param possibleSkeds
	 * @param possibleRooms
	 * @param courseCode
	 * @return
	 */
	private RoomAndSkedPair getRoomAndSked(final String roomCategory,
			final List<SchedulePair> possibleSkeds,
			List<ClassroomView> possibleRooms, final String courseCode,
			final int capacityNeeded) {
		RoomAndSkedPair roomAndSked = null;
		final int roundedCapacity = (int) Math.floor(capacityNeeded / 10) * 10;
		final List<ClassroomView> newPossibleRooms = new ArrayList<ClassroomView>();

		if (roomCategory != null) {
			if (possibleRooms.size() > 0) {
				roomAndSked = this.searchForRoomAndSked(possibleSkeds,
						possibleRooms, courseCode);
			}

			if (roomAndSked == null || possibleRooms.size() < 1) {
				try {
					possibleRooms = this.roomsList.get(roomCategory);
					for (final Iterator<ClassroomView> roomIter = possibleRooms
							.iterator(); roomIter.hasNext();) {
						final ClassroomView room = roomIter.next();

						// edit the condition if you want to bound the capacity
						// of
						// rooms
						if (room.getCapacity() >= roundedCapacity
								&& room.getCapacity() <= 2 * capacityNeeded) {
							newPossibleRooms.add(room);
						}
					}
				} catch (final NullPointerException ne) {
					System.out.println("roomCategory " + roomCategory);
				}

				if (newPossibleRooms.size() > 0) {
					roomAndSked = this.searchForRoomAndSked(possibleSkeds,
							newPossibleRooms, courseCode);
				}

				if (roomAndSked == null) {
					roomAndSked = new RoomAndSkedPair(null, possibleSkeds
							.get(0));
				}
			}
		} else {
			roomAndSked = new RoomAndSkedPair(null, possibleSkeds.get(0));
		}

		return roomAndSked;
	}

	/**
	 * Gets the smallest maximum number of students in the given courses
	 * 
	 * @param subjects
	 * @return
	 */
	private int getSmallestMaxStudents(final Set<CourseView> subjects) {
		int minStudents = this.students.size();
		int toCompare;

		for (final Iterator<CourseView> subjIter = subjects.iterator(); subjIter
				.hasNext();) {
			final CourseView subject = subjIter.next();
			if (subject.getHasLaboratory()) {
				toCompare = subject.getMaximumNumberOfLaboratoryStudents();
			} else {
				toCompare = subject.getMaximumNumberOfLectureStudents();
			}

			if (minStudents > toCompare && toCompare != 0) {
				minStudents = toCompare;
			}
		}

		return minStudents;
	}

	/**
	 * Separates subjects that have been already scheduled with those that are
	 * not
	 * 
	 * @param withSchedule
	 * @param withoutSchedule
	 * @param subjects
	 */
	private List<String> identifyScheduled(final Set<CourseView> withSchedule,
			final Set<CourseView> withoutSchedule, final List<String> subjects) {
		final List<CourseView> withoutSkedWithoutLab = new ArrayList<CourseView>();
		final List<CourseView> withSkedWithoutLab = new ArrayList<CourseView>();
		final List<String> unscheduledSubjects = new ArrayList<String>();

		for (final Iterator<String> subjectIter = subjects.iterator(); subjectIter
				.hasNext();) {
			final String courseCode = subjectIter.next();
			final CourseView subject = this.subjectsList.get(courseCode);

			if (subject != null) {
				if (subject.getNumOfLec() > 0) {
					if (subject.getHasLaboratory()) {
						withSchedule.add(subject);
					} else {
						withSkedWithoutLab.add(subject);
					}
				} else {
					if (subject.getHasLaboratory()) {
						withoutSchedule.add(subject);
					} else {
						withoutSkedWithoutLab.add(subject);
					}
				}
			} else {
				if (!this.subjectsWithMissingInfo.contains(courseCode)) {
					// System.out.println("Missing subject information for "
					// + courseCode + ". Kindly update the database.");
					this.subjectsWithMissingInfo.add(courseCode);
				}
				unscheduledSubjects.add(courseCode);
			}
		}

		withSchedule.addAll(withSkedWithoutLab);
		withoutSchedule.addAll(withoutSkedWithoutLab);

		return unscheduledSubjects;
	}

	/**
	 * Initializes degree courses
	 * 
	 * @throws Exception
	 */
	private void initDegreeCoursesList() throws Exception {
		this.degreeCoursesList = StudentViewPeer.getAllDegrees();
		System.out.println(this.degreeCoursesList.size()
				+ " Degree Courses have been found");
	}

	/**
	 * Initializes all needed values
	 * 
	 * @throws Exception
	 */
	private void initialize() throws Exception {
		this.initRoomsList();
		this.initSubjectsList();
		this.initDegreeCoursesList();
		this.initNonMajorCoursesList();
	}

	/**
	 * Initializes the list of non-major courses
	 * 
	 * @throws Exception
	 */
	private void initNonMajorCoursesList() throws Exception {
		this.nonMajorCoursesList = CourseViewPeer.getNonMajorCourses();
		System.out.println(this.nonMajorCoursesList.size()
				+ " Non-Major Courses have been identified");
	}

	/**
	 * Initializes rooms list
	 * 
	 * @throws Exception
	 */
	private void initRoomsList() throws Exception {
		this.roomsList = ClassroomViewPeer.getCategorizedRooms();
		System.out.println(this.roomsList.size()
				+ " Rooms have been initialized");
	}

	/**
	 * Initializes courses list
	 * 
	 * @throws Exception
	 */
	private void initSubjectsList() throws Exception {
		this.subjectsList = CourseViewPeer
				.getAllSubjects(this.subjectsWithMissingInfo);
		System.out.println(this.subjectsList.size()
				+ " Subjects have been initialized");
	}

	/**
	 * Prints the summary of the program
	 */
	private void printSummary() {
		this.printUnderloadedStudents();
		this.printUnscheduledStudents();
	}

	/**
	 * Prints to SCA_underloaded_students.csv file the underloaded students and
	 * their unscheduled courses
	 */
	private void printUnderloadedStudents() {
		final DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
		final java.util.Date date = new java.util.Date();
		final String datetime = dateFormat.format(date);
		final File outFile = new File("SCA_underloaded_students_" + datetime
				+ ".csv");

		try {
			final BufferedWriter output = new BufferedWriter(new FileWriter(
					outFile));
			output
					.write("SUMMARY OF UNDERLOADED STUDENTS ON STUDENT CLUSTERING ALGORITHM");
			output.newLine();
			output.write("Generated on " + datetime);
			output.newLine();
			output.newLine();
			output.write("STUDENT NO.,LACK UNITS,UNSCHEDULED COURSES");
			output.newLine();

			for (final Iterator<StudentView> studIter = this.finalStudentsWithUnscheduledCourses
					.iterator(); studIter.hasNext();) {
				final StudentView student = studIter.next();
				final Set<String> unscheduledCourses = student
						.getUnscheduledCourses();
				final StringBuffer unscheduledCoursesString = new StringBuffer();
				int lackUnits = 0;

				for (final Iterator<String> courseIter = unscheduledCourses
						.iterator(); courseIter.hasNext();) {
					final String course = courseIter.next();

					unscheduledCoursesString.append(course);
					unscheduledCoursesString.append(" + ");

					try {
						lackUnits += this.subjectsList.get(course).getCredit();
					} catch (final NullPointerException ne) {
						ne.printStackTrace();
					}

					if (this.unscheduledStudentsMap.containsKey(course)) {
						this.unscheduledStudentsMap.put(course,
								this.unscheduledStudentsMap.get(course) + 1);
					} else {
						this.unscheduledStudentsMap.put(course, 1);
					}
				}

				output.write(student.getStudentNumber() + "," + lackUnits + ","
						+ unscheduledCoursesString);
				output.newLine();
			}

			output.close();
		} catch (final IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * Prints to SCA_unscheduled_per_course.csv file the number of unscheduled
	 * students per course
	 */
	private void printUnscheduledStudents() {
		final DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
		final java.util.Date date = new java.util.Date();
		final String datetime = dateFormat.format(date);
		final File outFile = new File("SCA_unscheduled_per_course_" + datetime
				+ ".csv");
		final Set<String> coursecodes = this.unscheduledStudentsMap.keySet();
		int totalUnscheduled = 0;

		try {
			final BufferedWriter output = new BufferedWriter(new FileWriter(
					outFile));
			output
					.write("SUMMARY OF UNSCHEDULED STUDENTS PER COURSE ON STUDENT CLUSTERING ALGORITHM");
			output.newLine();
			output.write("Generated on " + datetime);
			output.newLine();
			output.newLine();
			output.write("COURSE,UNSCHEDULED STUDENTS");
			output.newLine();

			for (final Iterator<String> courseIter = coursecodes.iterator(); courseIter
					.hasNext();) {
				final String course = courseIter.next();
				final int unscheduled = this.unscheduledStudentsMap.get(course);

				totalUnscheduled += unscheduled;

				output.write(course + "," + unscheduled);
				output.newLine();
			}
			output.close();
		} catch (final IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Schedules a cluster
	 * 
	 * @param cluster
	 */
	private void schedule(final StudentCluster cluster) {
		final List<StudentCluster> studentClusters = this
				.scheduleCommonMajorCourses(cluster);

		for (final Iterator<StudentCluster> clusterIter = studentClusters
				.iterator(); clusterIter.hasNext();) {
			final StudentCluster studentCluster = clusterIter.next();
			studentCluster.copyScheduleToStudents();
			// System.out.println(studentCluster.toStringSubjects());
			// System.out.println(studentCluster.toStringUnscheduled());
			// System.out.println(studentCluster.toStringStudents());
			// studentCluster.getSchedule().print();
			this.scheduleNonMajorCourses(studentCluster);
			// System.out.println("===========================");
		}
	}

	/**
	 * Schedules the common major courses of the cluster
	 * 
	 * @param cluster
	 */
	private List<StudentCluster> scheduleCommonMajorCourses(
			final StudentCluster cluster) {
		final Set<CourseView> withSchedule = new HashSet<CourseView>();
		Set<CourseView> unscheduled = new HashSet<CourseView>();
		List<StudentCluster> clusters = new ArrayList<StudentCluster>();

		if (cluster.getCommonMajorCourses().size() > 0) {

			cluster.addUnscheduledCourses(this.identifyScheduled(withSchedule,
					unscheduled, cluster.getCommonMajorCourses()));

			unscheduled.addAll(this.scheduleCoursesWithExistingSchedule(
					cluster, withSchedule));

			// TODO : checks whether a section can be created that will fit all
			// the
			// students in the cluster, also checks whether the number of
			// sections
			// created does not exceed the number of predicted sections
			// to check in a subject
			unscheduled = this.scheduleCoursesIfFitClusterSize(cluster,
					unscheduled);

			// divides the cluster so that the cluster can be accomodated in one
			// scheduled section
			if (unscheduled.size() > 0) {
				clusters = this.divideIntoMiniClusters(cluster, unscheduled);
			} else {
				clusters.add(cluster);
			}
		}

		return clusters;
	}

	/**
	 * Checks if the size of the cluster will fit the available slots of the
	 * previously scheduled sections
	 * 
	 * @param cluster
	 * @param courses
	 * @return
	 */
	private Set<CourseView> scheduleCoursesIfFitClusterSize(
			final StudentCluster cluster, final Set<CourseView> courses) {
		final Set<CourseView> unscheduled = new HashSet<CourseView>();
		boolean isInserted = false;
		final int clusterSize = cluster.size();

		for (final Iterator<CourseView> courseIter = courses.iterator(); courseIter
				.hasNext();) {
			final CourseView course = courseIter.next();
			isInserted = false;
			final Schedule lectureSkeds = new Schedule();
			final int lecDaysPerWeek = course.getLectureSessionsPerWeek();
			final String lecRoomCategory = course.getLectureRoomCategory();
			final String labRoomCategory = course.getLaboratoryRoomCategory();
			final String courseCode = course.getCourseCode();

			if (lecDaysPerWeek == 0) {
				// System.out.println(course.getCourseCode()
				// + " has invalid lecture sessions per week.");
				unscheduled.add(course);
				continue;
			}

			final int lecTimePerSession = Math.round(course
					.getLectureHoursPerWeek()
					* 2 / lecDaysPerWeek);
			final List<SectionLecture> lectures = course.getLectureSections();

			if (course.getHasLaboratory()) {
				if (clusterSize <= course
						.getMaximumNumberOfLaboratoryStudents()
						&& course.getNumOfLab() < course.getPredictedNumOfLab()) {

					final int maxLabPerLec = course.getMaxLabPerLec();
					final int labDaysPerWeek = course
							.getLaboratorySessionsPerWeek();

					if (labDaysPerWeek == 0) {
						// System.out.println(course.getCourseCode()
						// + " has invalid laboratory sessions per week.");
						unscheduled.add(course);
						continue;
					}

					final int labTimePerSession = Math.round(course
							.getLaboratoryHoursPerWeek()
							* 2 / labDaysPerWeek);

					final List<ClassroomView> possibleLecRooms = new ArrayList<ClassroomView>();
					final List<ClassroomView> possibleLabRooms = new ArrayList<ClassroomView>();

					for (final Iterator<SectionLecture> lectIter = lectures
							.iterator(); lectIter.hasNext();) {

						final SectionLecture lecture = lectIter.next();
						final List<SectionLaboratory> labs = lecture
								.getLabSections();
						final Schedule tempSked = new Schedule();

						// copy lecture sked in lecture skeds
						lectureSkeds.addScheduleWithoutCheck(lecture
								.getDaysInt(), lecture.getTimeInt(), lecture
								.getCourseCode());

						final ClassroomView candidateLecRoom = lecture
								.getRoom();

						if (candidateLecRoom != null
								&& !possibleLecRooms.contains(candidateLecRoom)) {
							possibleLecRooms.add(lecture.getRoom());
						}

						if (labs.size() < maxLabPerLec
								&& cluster.isVacant(lecture.getDaysInt(),
										lecture.getTimeInt())) {

							// TODO: schedule a section
							tempSked.addScheduleWithoutCheck(lecture
									.getDaysInt(), lecture.getTimeInt(),
									lecture.getCourseCode());

							for (final Iterator<SectionLaboratory> labIter = labs
									.iterator(); labIter.hasNext();) {

								final SectionLaboratory lab = labIter.next();
								// this is done so that no lab sections are
								// scheduled at the same time
								tempSked.addScheduleWithoutCheck(lab
										.getDaysInt(), lab.getTimeInt(), lab
										.getCourseCode());

								final ClassroomView candidateLabRoom = lab
										.getRoom();
								if (candidateLabRoom != null
										&& !possibleLabRooms
												.contains(candidateLabRoom)) {
									possibleLabRooms.add(candidateLabRoom);
								}
							}

							// find a sked not in conflict with the scheduled
							// lab sections and lecture in the cluster
							tempSked.copy(cluster.getSchedule());

							// TODO: copyRoom sked

							final List<SchedulePair> possibleLabSkeds = tempSked
									.findVacant(labDaysPerWeek,
											labTimePerSession, lecture
													.getCourseCode(),
											this.notYetImplementedCourses,
											course.getSubjectType());
							if (possibleLabSkeds != null) {

								// get the best schedule
								final RoomAndSkedPair match = this
										.getRoomAndSked(
												labRoomCategory,
												possibleLabSkeds,
												possibleLabRooms,
												courseCode,
												course
														.getMaximumNumberOfLaboratoryStudents());

								if (match != null) {
									final SchedulePair labSked = match
											.getSchedule();
									final ClassroomView labRoom = match
											.getRoom();

									final SectionLaboratory labSection = new SectionLaboratory(
											lecture.getCourseCode(), lecture
													.getLecSectionName());

									labSection.setDaysInt(labSked.getDaysInt());
									labSection.setTimeInt(labSked.getTimeInt());
									labSection.setLabSectionName(lecture
											.getLecSectionName()
											+ "-L");
									labSection.setStudents(cluster
											.getStudents());
									labSection.setRoom(labRoom);

									lecture.addLabSection(labSection);
									course
											.setNumOfLab(course.getNumOfLab() + 1);
									lecture.addStudents(cluster.getStudents());
									isInserted = true;

									// updates the cluster sked
									cluster.addSchedule(labSked.getDaysInt(),
											labSked.getTimeInt(), labSection
													.getCourseCode());
									cluster.addSchedule(lecture.getDaysInt(),
											lecture.getTimeInt(), lecture
													.getCourseCode());
									break;
								} else {
									System.out
											.println("LAB ROOM NOT FOUND^^^^^^^^^^^^^^^^^^^^^^"
													+ course.getCourseCode());
								}
							}
						}
					}

					// TODO:if(di pa rin na sked) gawa na ng bago lecture
					if (!isInserted) {
						int maxNumOfLec = 0;

						// checks whether max number of sections to be scheduled
						// is
						// specified or not
						if (course.getMaxNumberOfLectureSections() == -1) {
							maxNumOfLec = course.getPredictedNumOfLec();
						} else {
							maxNumOfLec = Math.min(course
									.getMaxNumberOfLectureSections(), course
									.getPredictedNumOfLec());
						}

						if (course.getNumOfLec() < maxNumOfLec) {
							final Schedule tempSked = new Schedule();
							tempSked.copy(lectureSkeds);
							tempSked.copy(cluster.getSchedule());

							final List<SchedulePair> possibleLecSkeds = tempSked
									.findVacant(lecDaysPerWeek,
											lecTimePerSession, course
													.getCourseCode(),
											this.notYetImplementedCourses,
											course.getSubjectType());
							if (possibleLecSkeds != null) {
								RoomAndSkedPair match = this
										.getRoomAndSked(
												lecRoomCategory,
												possibleLecSkeds,
												possibleLecRooms,
												courseCode,
												course
														.getMaximumNumberOfLectureStudents());

								if (match == null) {
									System.out
											.println("LEC ROOM NOT FOUND^^^^^^^^^^^^^^^^^^^^^^"
													+ course.getCourseCode());
									cluster.addUnscheduledCourse(course
											.getCourseCode());
									continue;
								}

								final SchedulePair lecSked = match
										.getSchedule();
								final ClassroomView lecRoom = match.getRoom();

								final SectionLecture lecSection = new SectionLecture(
										course.getCourseCode(), "SECTION");
								lecSection.setDaysInt(lecSked.getDaysInt());
								lecSection.setTimeInt(lecSked.getTimeInt());
								lecSection.setRoom(lecRoom);

								tempSked.addScheduleWithoutCheck(lecSked
										.getDaysInt(), lecSked.getTimeInt(),
										lecSection.getCourseCode());

								final List<SchedulePair> possibleLabSkeds = tempSked
										.findVacant(labDaysPerWeek,
												labTimePerSession, course
														.getCourseCode(),
												this.notYetImplementedCourses,
												course.getSubjectType());
								if (possibleLabSkeds != null) {
									// get the best schedule (it is the one
									// placed
									// at the first of the list returned)

									match = this
											.getRoomAndSked(
													labRoomCategory,
													possibleLabSkeds,
													possibleLabRooms,
													courseCode,
													course
															.getMaximumNumberOfLaboratoryStudents());

									if (match != null) {
										final SchedulePair labSked = match
												.getSchedule();
										final ClassroomView labRoom = match
												.getRoom();

										final SectionLaboratory labSection = new SectionLaboratory(
												lecSection.getCourseCode(),
												lecSection.getLecSectionName());

										labSection.setDaysInt(labSked
												.getDaysInt());
										labSection.setTimeInt(labSked
												.getTimeInt());
										labSection.setLabSectionName(lecSection
												.getLecSectionName()
												+ "-L");
										labSection.setStudents(cluster
												.getStudents());
										labSection.setRoom(labRoom);

										isInserted = true;

										lecSection.addLabSection(labSection);
										course
												.setNumOfLab(course
														.getNumOfLab() + 1);
										lecSection.addStudents(cluster
												.getStudents());

										// updates the cluster sked
										cluster.addSchedule(labSked
												.getDaysInt(), labSked
												.getTimeInt(), labSection
												.getCourseCode());
										cluster.addSchedule(lecSection
												.getDaysInt(), lecSection
												.getTimeInt(), lecSection
												.getCourseCode());

										course.addSection(lecSection);
									} else {
										System.out
												.println("LAB ROOM NOT FOUND^^^^^^^^^^^^^^^^^^^^^^"
														+ course
																.getCourseCode());
										cluster.addUnscheduledCourse(course
												.getCourseCode());
									}
								}
							}
						} else {
							// System.out
							// .println("Cannot create more lecture skeds for "
							// + course.getCourseCode()
							// + " scheduled Lec: "
							// + course.getNumOfLec()
							// + " predicted: "
							// + course.getPredictedNumOfLec());
						}
					}
				}

			} else {
				// TODO: courses with no laboratory
				final Schedule tempSked = new Schedule();
				int maxNumOfLec = 0;

				// checks whether max number of sections to be scheduled
				// is
				// specified or not
				if (course.getMaxNumberOfLectureSections() == -1) {
					maxNumOfLec = course.getPredictedNumOfLec();
				} else {
					maxNumOfLec = Math.min(course
							.getMaxNumberOfLectureSections(), course
							.getPredictedNumOfLec());
				}

				if (clusterSize <= course.getMaximumNumberOfLectureStudents()
						&& course.getNumOfLec() < maxNumOfLec) {

					final List<ClassroomView> possibleLecRooms = new ArrayList<ClassroomView>();

					for (final Iterator<SectionLecture> lectIter = lectures
							.iterator(); lectIter.hasNext();) {

						final SectionLecture lecture = lectIter.next();

						final ClassroomView candidateLecRoom = lecture
								.getRoom();
						if (candidateLecRoom != null
								&& !possibleLecRooms.contains(candidateLecRoom)) {
							possibleLecRooms.add(lecture.getRoom());
						}

						// copy lecture sked in lecture skeds
						lectureSkeds.addScheduleWithoutCheck(lecture
								.getDaysInt(), lecture.getTimeInt(), lecture
								.getCourseCode());

					}

					tempSked.copy(lectureSkeds);
					tempSked.copy(cluster.getSchedule());

					final List<SchedulePair> possibleLecSkeds = tempSked
							.findVacant(lecDaysPerWeek, lecTimePerSession,
									course.getCourseCode(),
									this.notYetImplementedCourses, course
											.getSubjectType());

					if (possibleLecSkeds != null) {

						final RoomAndSkedPair match = this.getRoomAndSked(
								lecRoomCategory, possibleLecSkeds,
								possibleLecRooms, courseCode, course
										.getMaximumNumberOfLectureStudents());

						if (match != null) {
							final SchedulePair lecSked = match.getSchedule();
							final ClassroomView lecRoom = match.getRoom();

							final SectionLecture lecSection = new SectionLecture(
									course.getCourseCode(), "SECTION");
							lecSection.setDaysInt(lecSked.getDaysInt());
							lecSection.setTimeInt(lecSked.getTimeInt());
							lecSection.setRoom(lecRoom);

							isInserted = true;

							lecSection.addStudents(cluster.getStudents());

							// updates cluster sked
							cluster.addSchedule(lecSection.getDaysInt(),
									lecSection.getTimeInt(), lecSection
											.getCourseCode());

							course.addSection(lecSection);
						} else {
							System.out
									.println("LEC ROOM NOT FOUND^^^^^^^^^^^^^^^^^^^^^^"
											+ course.getCourseCode());
							cluster
									.addUnscheduledCourse(course
											.getCourseCode());
						}
					}

				} else {
					// System.out
					// .println("Cluster size too big or Cannot create more lecture skeds for "
					// + course.getCourseCode());
				}

			}

			if (!isInserted) {
				// System.out.println("SUBJECT " + course.getCourseCode()
				// + " has not been scheduled in creating sections");
				unscheduled.add(course);
			}
		}

		return unscheduled;
	}

	/**
	 * Schedules all the courses with existing course sections
	 * 
	 * @param cluster
	 * @param withSchedule
	 * @return
	 */
	private Set<CourseView> scheduleCoursesWithExistingSchedule(
			final StudentCluster cluster, final Set<CourseView> withSchedule) {
		final Set<CourseView> unscheduled = new HashSet<CourseView>();
		boolean isInserted = false;
		final int clusterSize = cluster.size();

		for (final Iterator<CourseView> subjIter = withSchedule.iterator(); subjIter
				.hasNext();) {
			final CourseView subject = subjIter.next();
			isInserted = false;

			/* the subject has laboratory */
			if (subject.getHasLaboratory()) {
				final List<SectionLecture> sectionWithLab = subject
						.getLectureSections();

				for (final Iterator<SectionLecture> lecIter = sectionWithLab
						.iterator(); lecIter.hasNext();) {
					final SectionLecture lecture = lecIter.next();
					final List<SectionLaboratory> labSections = lecture
							.getLabSections();

					/* checks whether lecture sked will fit the cluster sked */
					if (cluster.isVacant(lecture.getDaysInt(), lecture
							.getTimeInt())) {
						for (final Iterator<SectionLaboratory> labIter = labSections
								.iterator(); labIter.hasNext();) {
							final SectionLaboratory lab = labIter.next();

							/*
							 * checks if all the students in the cluster will
							 * fit the SectionLecture and if the schedule of the
							 * cluster is vacant in the time scheduled for the
							 * lab, the cluster is scheduled on that time
							 */
							if (clusterSize <= subject
									.getMaximumNumberOfLaboratoryStudents()
									- lab.size()
									&& cluster.isVacant(lab.getDaysInt(), lab
											.getTimeInt())) {

								lab.addStudents(cluster.getStudents());
								lecture.addStudents(cluster.getStudents());
								isInserted = true;

								/*
								 * updates the schedule of the cluster with the
								 * schedule of the lecture
								 */
								cluster.addSchedule(lab.getDaysInt(), lab
										.getTimeInt(), lab.getCourseCode());
								cluster.addSchedule(lecture.getDaysInt(),
										lecture.getTimeInt(), lecture
												.getCourseCode());

								break;
							}
						}
					}

					if (isInserted) {
						break;
					}
				}

			} else {
				/* subject does not have a laboratory */
				final List<SectionLecture> sections = subject
						.getLectureSections();

				for (final Iterator<SectionLecture> lecIter = sections
						.iterator(); lecIter.hasNext();) {
					final SectionLecture lecture = lecIter.next();

					/* checks whether lecture sked will fit the cluster sked */
					if (clusterSize <= subject
							.getMaximumNumberOfLectureStudents()
							- lecture.size()
							&& cluster.isVacant(lecture.getDaysInt(), lecture
									.getTimeInt())) {

						lecture.addStudents(cluster.getStudents());
						isInserted = true;

						/*
						 * updates the schedule of the cluster with the schedule
						 * of the lecture
						 */
						cluster.addSchedule(lecture.getDaysInt(), lecture
								.getTimeInt(), lecture.getCourseCode());

						break;
					}
				}
			}

			/* if subject has not been scheduled */
			if (!isInserted) {
				// System.out.println("SUBJECT " + subject.getCourseCode()
				// + " has not been scheduled with existing schedule");
				unscheduled.add(subject);
			}
		}

		return unscheduled;
	}

	/**
	 * Schedules the student to an existing course section
	 * 
	 * @param student
	 * @param course
	 * @return
	 */
	private boolean scheduleCourseWithExistingSchedule(
			final StudentView student, final CourseView course) {
		boolean isInserted = false;
		final List<SectionLecture> lectSections = course.getLectureSections();

		if (course.getHasLaboratory()) {

			for (final Iterator<SectionLecture> lecIter = lectSections
					.iterator(); lecIter.hasNext();) {
				final SectionLecture lecture = lecIter.next();
				final List<SectionLaboratory> labSections = lecture
						.getLabSections();

				/* checks whether lecture sked will fit the cluster sked */
				if (student
						.isVacant(lecture.getDaysInt(), lecture.getTimeInt())) {
					for (final Iterator<SectionLaboratory> labIter = labSections
							.iterator(); labIter.hasNext();) {
						final SectionLaboratory lab = labIter.next();

						if (lab.size() < course
								.getMaximumNumberOfLaboratoryStudents()
								&& student.isVacant(lab.getDaysInt(), lab
										.getTimeInt())) {

							lab.addStudent(student);
							lecture.addStudent(student);
							isInserted = true;

							/*
							 * updates the schedule of the student with the
							 * schedule of the lecture and lab
							 */
							student.addSchedule(lab.getDaysInt(), lab
									.getTimeInt(), lab.getCourseCode());
							student.addSchedule(lecture.getDaysInt(), lecture
									.getTimeInt(), lecture.getCourseCode());
							break;
						}
					}
				}
			}
		} else {
			/* subject does not have a laboratory */

			for (final Iterator<SectionLecture> lecIter = lectSections
					.iterator(); lecIter.hasNext();) {
				final SectionLecture lecture = lecIter.next();

				/* checks whether lecture sked will fit the student sked */
				if (lecture.size() < course.getMaximumNumberOfLectureStudents()
						&& student.isVacant(lecture.getDaysInt(), lecture
								.getTimeInt())) {

					lecture.addStudent(student);
					isInserted = true;

					/*
					 * updates the schedule of the student with the schedule of
					 * the lecture
					 */
					student.addSchedule(lecture.getDaysInt(), lecture
							.getTimeInt(), lecture.getCourseCode());

					break;
				}
			}
		}
		/* returns if the course is Inserted */
		return isInserted;
	}

	/**
	 * Schedules the unscheduled courses of the miniCluster
	 * 
	 * @param miniCluster
	 * @param unscheduled
	 */
	private void scheduleMiniClusters(final StudentCluster miniCluster,
			Set<CourseView> unscheduledCourses) {

		unscheduledCourses = this.scheduleCoursesWithExistingSchedule(
				miniCluster, unscheduledCourses);

		// TODO : checks whether a section can be created that will fit all the
		// students in the cluster, also checks whether the number of sections
		// created does not exceed the number of predicted sections
		// to check in a subject
		unscheduledCourses = this.scheduleCoursesIfFitClusterSize(miniCluster,
				unscheduledCourses);

		if (unscheduledCourses.size() > 0) {
			// System.out.println("UNSCHEDULED COURSES OF MINI");
			for (final Iterator<CourseView> subjIter = unscheduledCourses
					.iterator(); subjIter.hasNext();) {
				miniCluster.addUnscheduledCourse(subjIter.next()
						.getCourseCode());
			}

		}
	}

	/**
	 * Schedules the non-major courses of a cluster
	 * 
	 * @param cluster
	 */
	private void scheduleNonMajorCourses(final StudentCluster cluster) {
		final List<StudentView> students = cluster.getStudents();
		for (final Iterator<StudentView> studIter = students.iterator(); studIter
				.hasNext();) {
			final StudentView student = studIter.next();
			this.scheduleNonMajorCoursesOfStudent(student);
		}
	}

	/**
	 * Schedules non-major courses of the student
	 * 
	 * @param student
	 */
	private void scheduleNonMajorCoursesOfStudent(final StudentView student) {
		final List<String> nonMajorCoursesString = student
				.getRecommendedCourses().getNonMajorCourses();

		if (nonMajorCoursesString.size() < 1) {
			return;
		} else {
			final List<CourseView> nonMajorCourses = this
					.getCourses(nonMajorCoursesString);
			for (final Iterator<CourseView> courseIter = nonMajorCourses
					.iterator(); courseIter.hasNext();) {
				final CourseView nonMajor = courseIter.next();
				if (!this.scheduleCourseWithExistingSchedule(student, nonMajor)) {
					if (!this.createScheduleFor(student, nonMajor)) {
						student.addUnscheduledCourse(nonMajor.getCourseCode());
					}
				}
			}
		}

		if (student.getUnscheduledCourses().size() > 0) {
			this.studentsWithUnscheduledCourses.add(student);
		}
	}

	/**
	 * Tries to schedule the unscheduled courses of the student for the last
	 * time
	 * 
	 * @param student
	 */
	private void scheduleUnscheduledCoursesOfStudent(final StudentView student) {
		final Set<String> unscheduledString = student.getUnscheduledCourses();
		final List<String> unscheduledList = new ArrayList<String>();

		for (final Iterator<String> courseIter = unscheduledString.iterator(); courseIter
				.hasNext();) {
			final String course = courseIter.next();
			if (course != null) {
				unscheduledList.add(course);
			}
		}

		if (unscheduledList.size() < 1) {
			return;
		} else {
			final List<CourseView> unscheduledCourses = this
					.getCourses(unscheduledList);
			student.setUnscheduledCourses(new HashSet<String>());
			for (final Iterator<CourseView> courseIter = unscheduledCourses
					.iterator(); courseIter.hasNext();) {
				final CourseView unscheduled = courseIter.next();
				if (!this.scheduleCourseWithExistingSchedule(student,
						unscheduled)) {
					if (!this.createScheduleFor(student, unscheduled)) {
						student.addUnscheduledCourse(unscheduled
								.getCourseCode());
					}
				}
			}
		}

		if (student.getUnscheduledCourses().size() > 0) {
			this.finalStudentsWithUnscheduledCourses.add(student);
		}
	}

	/**
	 * Iterates through the possible rooms and possible schedules and tries to
	 * find a match
	 * 
	 * @param sked
	 * @param classroom
	 * @param possibleSkeds
	 * @param possibleRooms
	 * @param courseCode
	 * @return true if there is a matching room and schedule, false otherwise
	 */
	private RoomAndSkedPair searchForRoomAndSked(
			final List<SchedulePair> possibleSkeds,
			final List<ClassroomView> possibleRooms, final String courseCode) {
		RoomAndSkedPair roomAndSked = null;
		boolean hasFound = false;

		for (final Iterator<SchedulePair> pairIter = possibleSkeds.iterator(); pairIter
				.hasNext();) {
			final SchedulePair pair = pairIter.next();

			for (final Iterator<ClassroomView> roomIter = possibleRooms
					.iterator(); roomIter.hasNext();) {

				final ClassroomView room = roomIter.next();
				if (room.getSchedule().isVacant(pair.getDaysInt(),
						pair.getTimeInt())) {

					room.getSchedule().addSchedule(pair.getDaysInt(),
							pair.getTimeInt(), courseCode);

					roomAndSked = new RoomAndSkedPair(room, pair);

					hasFound = true;
					break;
				}
			}
			if (hasFound) {
				break;
			}
		}

		return roomAndSked;
	}

	/**
	 * The main method of the algorithm
	 * 
	 * @throws Exception
	 */
	public void start() throws Exception {
		this.initialize();

		System.out.println("INITIALIZATION FINISHED\n");

		/* iterate over all degree courses */
		// for (Iterator<String> degreeIter = degreeCoursesList.iterator();
		// degreeIter
		// .hasNext();) {
		// String degree = degreeIter.next();
		// System.out.println("Processing " + degree + " Students...");
		this.students = new ArrayList<StudentView>();
		/* get all students with the current degree */
		// students = StudentViewPeer.getStudentsWithDegree(degree);
		this.students = StudentViewPeer.getStudentsWithAnyDegree();

		this.studentClusters = new ArrayList<StudentCluster>();
		/* cluster students list */
		this.studentClusters = this.clusterStudents(this.students);

		for (final Iterator<StudentCluster> clusterIter = this.studentClusters
				.iterator(); clusterIter.hasNext();) {
			final StudentCluster cluster = clusterIter.next();

			this.schedule(cluster);
		}

		// }

		for (final Iterator<StudentView> studIter = this.studentsWithUnscheduledCourses
				.iterator(); studIter.hasNext();) {
			final StudentView student = studIter.next();

			this.scheduleUnscheduledCoursesOfStudent(student);
		}

		this.writeToDatabase();

		this.printSummary();
	}

	/**
	 * Writes the students slots information on the database
	 * 
	 * @param students
	 * @param sectionId
	 * @param size
	 */
	private void writeStudents(final List<StudentView> students,
			final int sectionId, final int size) {
		int ctr = 0;
		for (final Iterator<StudentView> studIter = students.iterator(); studIter
				.hasNext();) {
			final Slot slot = new Slot();
			slot.setSectionId(sectionId);
			slot.setStudentNumber(studIter.next().getStudentNumber());
			slot.setStatus(SLOT_RESERVED);
			ctr++;

			Slot.add(slot);
		}

		for (int i = ctr; i < size; i++) {
			final Slot slot = new Slot();
			slot.setSectionId(sectionId);
			slot.setStatus(SLOT_VACANT);

			Slot.add(slot);
		}

	}

	/**
	 * Writes the generated schedule information on the database and outputs a
	 * .csv file SCA_courses_scheduled
	 */
	private void writeToDatabase() {
		final DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
		final java.util.Date date = new java.util.Date();
		final String datetime = dateFormat.format(date);
		final File outFile = new File("SCA_courses_scheduled_" + datetime
				+ ".csv");

		// System.out.println(outFile.getAbsolutePath());
		try {
			final BufferedWriter output = new BufferedWriter(new FileWriter(
					outFile));
			output
					.write("SUMMARY OF COURSES SCHEDULED BY STUDENT CLUSTERING ALGORITHM");
			output.newLine();
			output.write("Generated on " + datetime);
			output.newLine();
			output.newLine();
			output
					.write("COURSE,% SCHEDULED,STUDENTS SCHEDULED,LEC SCHEDULED,LAB SCHEDULED,REMARKS");
			output.newLine();
			System.out.println("WRITING TO DB");
			for (final Iterator<CourseView> courseIter = this.subjectsList
					.values().iterator(); courseIter.hasNext();) {
				final CourseView course = courseIter.next();
				final List<SectionLecture> lectureSections = course
						.getLectureSections();
				int scheduledStudents = 0;
				int sectionId;
				int maxStudents;
				int lecCtr = 1;
				String prevSection = "";
				CourseDemandUtility.sortLectureSections(lectureSections);
				final List<String> sameSections = this
						.assignSections(lectureSections);

				for (final Iterator<SectionLecture> lectIter = lectureSections
						.iterator(); lectIter.hasNext();) {
					final SectionLecture lecture = lectIter.next();
					final String lectSection = lecture.getLecSectionName();
					if (!lectSection.equals(prevSection)) {
						lecCtr = 1;
					}
					if (sameSections != null
							&& sameSections.contains(lectSection)) {
						lecture.setLecSectionName(lectSection + lecCtr);
						lecCtr++;
					}

					prevSection = lectSection;

					lecture.setDays(ScheduleUtility
							.getDaysStringEquivalent(lecture.getDaysInt()));
					lecture.setTime(ScheduleUtility
							.getTimeStringEquivalent(lecture.getTimeInt()));
					scheduledStudents += lecture.size();

					if (course.getHasLaboratory()) {
						int ctr = 1;
						final List<SectionLaboratory> labSections = lecture
								.getLabSections();
						CourseDemandUtility.sortLabSections(labSections);

						for (final Iterator<SectionLaboratory> labIter = labSections
								.iterator(); labIter.hasNext();) {
							final SectionLaboratory lab = labIter.next();
							final StringBuffer sb = new StringBuffer();
							lab.setLecSectionName(lecture.getLecSectionName());
							sb.append(lab.getLecSectionName());
							sb.append("-");
							sb.append(ctr);
							sb.append("L");
							lab.setLabSectionName(sb.toString());
							lab.setDays(ScheduleUtility
									.getDaysStringEquivalent(lab.getDaysInt()));
							lab.setTime(ScheduleUtility
									.getTimeStringEquivalent(lab.getTimeInt()));
							ctr++;

							maxStudents = course
									.getMaximumNumberOfLaboratoryStudents();

							if (maxStudents == 0) {
								maxStudents = lab.getStudents().size();
							}

							sectionId = SectionView.add(lecture, lab,
									maxStudents);
							this.writeStudents(lab.getStudents(), sectionId,
									maxStudents);
						}
					} else {
						maxStudents = course
								.getMaximumNumberOfLectureStudents();

						if (maxStudents == 0) {
							maxStudents = lecture.getStudents().size();
						}

						sectionId = SectionView.add(lecture, maxStudents);
						this.writeStudents(lecture.getStudents(), sectionId,
								maxStudents);
					}
				}

				course.setScheduledStudents(scheduledStudents);
				if (course.getRecommendedTo() != 0) {
					output.write(course.toStringStatistics());

					if (this.notYetImplementedCourses.contains(course
							.getCourseCode())) {
						output.write("-->NOT YET IMPLEMENTED");
					} else if (this.subjectsWithMissingInfo.contains(course
							.getCourseCode())) {
						output.write("-->SUBJECT WITH MISSING INFO");
					}

					output.newLine();
				}
			}

			output.close();
		} catch (final IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
